package eiisan.forkjointest.countedcompleter;

import java.util.Random;
import java.util.concurrent.CountedCompleter;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;

public class Searcher<E> extends CountedCompleter<E> {
    final E[] array;
    final AtomicReference<E> result;
    final int lo, hi;
    final Predicate<E> predicate;

    public Searcher(CountedCompleter<?> p, E[] array, AtomicReference<E> result, int lo, int hi, Predicate<E> predicate) {
        super(p);
        this.array = array;
        this.result = result;
        this.lo = lo;
        this.hi = hi;
        this.predicate = predicate;
    }

    public E getRawResult() {
        return result.get();
    }

    public void compute() { // similar to ForEach version 3
        int l = lo, h = hi;
        while (result.get() == null && h >= l) {
            if (h - l >= 2) {
                int mid = (l + h) >>> 1;
                addToPendingCount(1);
                new Searcher(this, array, result, mid, h, predicate).fork();
                h = mid;
            } else {
                E x = array[l];
                if (matches(x,lo) && result.compareAndSet(null, x))
                    quietlyCompleteRoot(); // root task is now joinable
                break;
            }
        }
        tryComplete(); // normally complete whether or not found
    }

    boolean matches(E e, int idx) {
        System.out.println(Thread.currentThread().getName() + " matches ===> " + idx);
        boolean test = predicate.test(e);
        if (test) {
            System.out.println(idx);
        }
        return test;
    } // return true if found

    public static <E> E search(E[] array, Predicate<E> predicate) {
        return new Searcher<E>(null, array, new AtomicReference<E>(), 0, array.length,predicate).invoke();
    }
}
